class Vector2 {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }

    get length() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }

    set length(len) {
        return this.normalize().scale(len)
    }

    get angle() {
        // computes the angle in radians with respect to the positive x-axis
        const angle = Math.atan2(- this.y, - this.x) + Math.PI;

        return angle;
    }

    distanceTo(v) {
        return Math.sqrt(this.distanceToSquared(v));
    }

    distanceToSquared(v) {
        const dx = this.x - v.x, dy = this.y - v.y;
        return dx * dx + dy * dy;
    }

    set(x, y) {
        this.x = x;
        this.y = y;

        return this;
    }

    setX(x) {
        this.x = x;

        return this;
    }

    setY(y) {
        this.y = y;

        return this;
    }

    clone() {
        return new this.constructor(this.x, this.y);
    }

    copy(v) {
        this.x = v.x;
        this.y = v.y;

        return this;
    }

    equals(v) {
        return ((v.x === this.x) && (v.y === this.y));
    }

    fromArray(array, offset = 0) {
        this.x = array[offset];
        this.y = array[offset + 1];

        return this;
    }

    toArray(array = [], offset = 0) {
        array[offset] = this.x;
        array[offset + 1] = this.y;

        return array;
    }

    applyMatrix3(m) {
        const x = this.x, y = this.y;
        const e = m.elements;

        this.x = e[0] * x + e[3] * y + e[6];
        this.y = e[1] * x + e[4] * y + e[7];

        return this;
    }

    clamp(min, max) {
        // assumes min < max, componentwise
        this.x = Math.max(min.x, Math.min(max.x, this.x));
        this.y = Math.max(min.y, Math.min(max.y, this.y));

        return this;
    }

    add(v) {
        const p = v instanceof Vector2;
        this.x += p ? v.x : v;
        this.y += p ? v.y : v;
        return this;
    }

    sub(v) {
        const p = v instanceof Vector2;
        this.x -= p ? v.x : v;
        this.y -= p ? v.y : v;
        return this;
    }

    mul(v) {
        const p = v instanceof Vector2;
        this.x *= p ? v.x : v;
        this.y *= p ? v.y : v;
        return this;
    }

    scale(x) {
        this.x *= x;
        this.y *= x;
        return this;
    }

    dot(v) {
        return fomatFloat(this.x * v.x + this.y * v.y);
    }

    cross(v) {
        // A x B=|A|·|B|·sinα 用以判断该角度是正或者负
        // A x B=x1*y2-x2*y1 用于由A和B构成的平行四边形的面积，进而可以得到两个三角形的面积
        // A x B=x1*y2-x2*y1 用以得到垂直一平面的向量z,即法向量
        return fomatFloat(this.x * v.y - this.y * v.x);
    }

    normalize() {
        const len = this.length;
        if (len > 0) {
            this.x = fomatFloat(this.x / len);
            this.y = fomatFloat(this.y / len);
        }

        return this;
    }

    distance(v) {
        const x = this.x - v.x;
        const y = this.y - v.y;
        return Math.sqrt(x * x + y * y);
    }

    rotateAround(angle, center = { x: 0, y: 0 }) {
        const c = Math.cos(angle), s = Math.sin(angle);

        const x = this.x - center.x;
        const y = this.y - center.y;

        this.x = fomatFloat(x * c - y * s + center.x)
        this.y = fomatFloat(x * s + y * c + center.y)

        return this;
    }

    random() {
        this.x = fomatFloat(Math.random());
        this.y = fomatFloat(Math.random());

        return this;
    }

    static add(v1, v2) {
        const v = v2 instanceof Vector2;
        return new Vector2(
            fomatFloat(v1.x + (v ? v2.x : v2)),
            fomatFloat(v1.y + (v ? v2.y : v2))
        );
    }

    static sub(v1, v2) {
        const v = v2 instanceof Vector2;
        return new Vector2(
            fomatFloat(v1.x - (v ? v2.x : v2)),
            fomatFloat(v1.y - (v ? v2.y : v2))
        );
    }

    static mul(v1, v2) {
        const v = v2 instanceof Vector2;
        return new Vector2(
            fomatFloat(v1.x * (v ? v2.x : v2)),
            fomatFloat(v1.y * (v ? v2.y : v2))
        );
    }

    static dot(v1, v2) {
        return fomatFloat(v1.x * v2.x + v1.y * v2.y)
    }
    
    static cross(v1, v2) {
        return fomatFloat(v1.x * v2.y - v1.y * v2.x);
    }
}

function fomatFloat(num, pos = 10) {
    return Math.round(num * Math.pow(10, pos)) / Math.pow(10, pos);
}

export { Vector2 }